from src.compiler.Lexer.tokens import Token, TokenType, KEYWORDS


class LexerError(Exception):
    """词法分析错误异常类"""

    def __init__(self, message, line, column):
        super().__init__(f"{message} at line {line}, column {column}")
        self.line = line
        self.column = column


class Lexer:
    """词法分析器"""

    def __init__(self, text):
        self.text = text
        self.pos = 0
        self.line = 1
        self.column = 1
        self.current_char = self.text[0] if self.text else None

    def error(self, message):
        """抛出词法错误"""
        raise LexerError(message, self.line, self.column)

    def advance(self):
        """前进一个字符，更新行列号"""
        if self.current_char == '\n':
            self.line += 1
            self.column = 1
        else:
            self.column += 1

        self.pos += 1
        if self.pos < len(self.text):
            self.current_char = self.text[self.pos]
        else:
            self.current_char = None

    def skip_whitespace(self):
        """跳过空白字符"""
        while self.current_char is not None and self.current_char.isspace():
            self.advance()

    def skip_comment(self):
        """跳过注释"""
        start_line = self.line
        start_column = self.column

        # 处理单行注释 --
        if self.current_char == '-' and self.pos + 1 < len(self.text) and self.text[self.pos + 1] == '-':
            self.advance()  # 跳过第一个 -
            self.advance()  # 跳过第二个 -

            # 跳过直到行尾
            while self.current_char is not None and self.current_char != '\n':
                self.advance()

            if self.current_char == '\n':
                self.advance()  # 跳过换行符

        # 处理多行注释 /* */
        elif self.current_char == '/' and self.pos + 1 < len(self.text) and self.text[self.pos + 1] == '*':
            self.advance()  # 跳过 /
            self.advance()  # 跳过 *
            # 跳过直到遇到 */
            while self.current_char is not None:
                if self.current_char == '*' and self.pos + 1 < len(self.text) and self.text[self.pos + 1] == '/':
                    self.advance()  # 跳过 *
                    self.advance()  # 跳过 /
                    break
                self.advance()
            else:
                self.error("Unclosed multi-line comment")
        else:
            self.error("Expected comment")

    def number(self):
        """读取数字常量"""
        start_pos = self.pos
        start_column = self.column

        while self.current_char is not None and self.current_char.isdigit():
            self.advance()

        # 暂时不支持浮点数
        number_str = self.text[start_pos:self.pos]
        return Token(TokenType.CONSTANT_NUMBER, number_str, self.line, start_column)

    def string(self):
        """读取字符串常量"""
        start_line = self.line
        start_column = self.column
        delimiter = self.current_char
        self.advance()  # 跳过开始的引号

        result = []
        while self.current_char is not None and self.current_char != delimiter:
            result.append(self.current_char)
            self.advance()

        if self.current_char != delimiter:
            self.error("Unclosed string literal")

        self.advance()  # 跳过结束的引号

        string_value = ''.join(result)
        return Token(TokenType.CONSTANT_STRING, string_value, start_line, start_column)

    def identifier(self):
        """读取标识符或关键字"""
        start_pos = self.pos
        start_column = self.column

        while (self.current_char is not None and
               (self.current_char.isalnum() or self.current_char == '_')):
            self.advance()

        identifier_str = self.text[start_pos:self.pos]

        # 检查是否是关键字
        token_type = KEYWORDS.get(identifier_str.upper())
        if token_type is not None:
            return Token(token_type, identifier_str, self.line, start_column)
        else:
            return Token(TokenType.IDENTIFIER, identifier_str, self.line, start_column)

    def get_next_token(self):
        """获取下一个Token"""
        while self.current_char is not None:
            if self.current_char.isspace():
                self.skip_whitespace()
                continue

            # 检查注释
            if self.current_char == '-':
                    # 检查是否是单行注释 --
                if self.pos + 1 < len(self.text) and self.text[self.pos + 1] == '-':
                    self.skip_comment()
                    continue

            if self.current_char == '/':
            # 检查是否是多行注释 /*
                if self.pos + 1 < len(self.text) and self.text[self.pos + 1] == '*':
                    self.skip_comment()
                    continue

            if self.current_char.isalpha() or self.current_char == '_':
                return self.identifier()

            if self.current_char.isdigit():
                return self.number()

            if self.current_char in ('"', "'"):
                return self.string()

            # 处理运算符和界符
            start_column = self.column

            # 多字符运算符
            if self.current_char == '!':
                self.advance()
                if self.current_char == '=':
                    self.advance()
                    return Token(TokenType.OPERATOR_NE, '!=', self.line, start_column)
                else:
                    self.error("Unexpected character '!'")

            if self.current_char == '>':
                self.advance()
                if self.current_char == '=':
                    self.advance()
                    return Token(TokenType.OPERATOR_GE, '>=', self.line, start_column)
                else:
                    return Token(TokenType.OPERATOR_GT, '>', self.line, start_column)

            if self.current_char == '<':
                self.advance()
                if self.current_char == '=':
                    self.advance()
                    return Token(TokenType.OPERATOR_LE, '<=', self.line, start_column)
                else:
                    return Token(TokenType.OPERATOR_LT, '<', self.line, start_column)

            # 单字符运算符和界符
            single_char_tokens = {
                '+': TokenType.OPERATOR_PLUS,
                '-': TokenType.OPERATOR_MINUS,
                '*': TokenType.OPERATOR_MUL,
                '/': TokenType.OPERATOR_DIV,
                '=': TokenType.OPERATOR_EQ,
                ',': TokenType.DELIMITER_COMMA,
                ';': TokenType.DELIMITER_SEMICOLON,
                '(': TokenType.DELIMITER_LPAREN,
                ')': TokenType.DELIMITER_RPAREN,
                '.': TokenType.DELIMITER_PERIOD
            }

            if self.current_char in single_char_tokens:
                char = self.current_char
                token_type = single_char_tokens[char]
                self.advance()
                return Token(token_type, char, self.line, start_column)

            self.error(f"Unexpected character '{self.current_char}'")

        return Token(TokenType.EOF, None, self.line, self.column)

    def tokenize(self):
        """将整个输入文本转换为Token列表"""
        tokens = []
        while True:
            token = self.get_next_token()
            tokens.append(token)
            if token.type == TokenType.EOF:
                break
        return tokens